package groot.dozer.common;

import groot.dozer.Enum.TypeEnum;
import groot.dozer.annotation.ClassMapping;
import groot.dozer.annotation.FieldMapping;
import groot.dozer.constant.ElementConstant;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * 自定义注解解析类
 * @author Groot
 * @since 2018/11/7 13:33
 *
 * @see ClassMapping
 * @see FieldMapping
 */
public class AnnotationParse {

  /**
   * 从Class中获取Annotation，解析到document元素中
   * @param mappingClass
   * @return
   */
  public Document parseClass(Class mappingClass) {
    ClassMapping classMapping = (ClassMapping)mappingClass.getAnnotation(ClassMapping.class);

    Document document = DocumentFactory.getInstance();
    Element mappings = createMappings(document);
    Element mapping = createMapping(document, classMapping);
    List<Element> classList = createClass(document,classMapping,mappingClass);
    List<Element> fieldList = createField(document,mappingClass,classMapping);
    combinationXml(document,mappings,mapping,classList,fieldList);

    return document;
  }

  /**
   * 按顺序排序node元素，构建document
   */
  private void combinationXml(Document document, Element mappings, Element mapping,
      List<Element> classList, List<Element> fieldList) {
    // 把classList,fieldList加进mapping中，
    for (Element classE : classList) {
      mapping.appendChild(classE);
    }
    for (Element fieldE : fieldList) {
      mapping.appendChild(fieldE);
    }
    // 把mapping加入mappings
    mappings.appendChild(mapping);
    // mappings加入document
    document.appendChild(mappings);

  }

  /**
   * 创建field元素节点
   */
  private List<Element> createField(Document document,Class mappingClass,ClassMapping classMapping) {
    List<Element> fieldList = new ArrayList();
    Field[] fields = mappingClass.getDeclaredFields();
    for (Field field : fields) {
      FieldMapping fieldAnnotation = field.getAnnotation(FieldMapping.class);
      if (fieldAnnotation != null) {
        field.setAccessible(true);
        Element fieldRoot = document.createElement(ElementConstant.FIELD);
        Element fieldA = document.createElement(ElementConstant.FIELD_CHILD_A);
        Element fieldB = document.createElement(ElementConstant.FIELD_CHILD_B);

          if (classMapping.isSource()) {
            fieldA.setTextContent(field.getName());
            fieldB.setTextContent(fieldAnnotation.value());
          } else {
          fieldA.setTextContent(fieldAnnotation.value());
          fieldB .setTextContent(field.getName());
        }
        fieldRoot.appendChild(fieldA);
        fieldRoot.appendChild(fieldB);
        fieldList.add(fieldRoot);

      }
    }

    return fieldList;
  }

  /**
   * 创建class元素节点
   */
  private List<Element> createClass(Document document, ClassMapping classMapping,Class mappingClass) {
    List<Element> classList = new ArrayList();
    Element classA = document.createElement(ElementConstant.CLASSA);
    Element classB = document.createElement(ElementConstant.CLASSB);
    // 此处是为处理单向映射，class为映射源对象
    if (classMapping.isSource()){
      classA.setTextContent(mappingClass.getName());
      classB.setTextContent(classMapping.value().getName());
    } else {
      classA.setTextContent(classMapping.value().getName());
      classB.setTextContent(mappingClass.getName());
    }
    classList.add(classA);
    classList.add(classB);
    return classList;


  }

  /**
   * 创建mappings元素节点
   */
  private Element createMappings(Document document){
    Element mappings = document.createElement(ElementConstant.MAPPINGS);
    return mappings;
  }

  /**
   * 创建mapping元素节点
   */
  private Element createMapping(Document document,ClassMapping classMapping){
    Element mapping = document.createElement(ElementConstant.MAPPING);

    if (StringUtils.isNotBlank(classMapping.dateFormat())){
      mapping.setAttribute(ElementConstant.DATE_FORMAT,classMapping.dateFormat());
    }
    mapping.setAttribute(ElementConstant.MAP_NULL,Boolean.toString(classMapping.mapNull()));
    mapping.setAttribute(ElementConstant.TYPE,classMapping.type().getValue());

    return mapping;
  }







}
